import { Vec2 } from "cc";

export default class GeometryUtils {
    public static InflatePolygon(points: Vec2[], dist: number): Vec2[] {
        let inflatedPoints = [];
		for (let i = 0; i < points.length; i++){
            let a = points[i == 0? points.length - 1 : i - 1];
            let b = points[i];
            let c = points[(i + 1) % points.length];

            let ab = Vec2.subtract(new Vec2(), a, b).normalize();
            let cb = Vec2.subtract(new Vec2(), c, b).normalize();
            let mid = ab.add(cb);
            mid.multiplyScalar(this.PointIsConcave(points, i)? dist : -dist);
            inflatedPoints[i] = Vec2.add(mid, points[i], mid);
		}
		return inflatedPoints;
    }

    public static PointIsConcave(points: Vec2[], pointIndex: number): boolean {
        let current = points[pointIndex];
		let next = points[(pointIndex + 1) % points.length];
		let previous =  points[pointIndex == 0? points.length - 1 : pointIndex - 1];
		let left = new Vec2(current.x - previous.x, current.y - previous.y);
		let right = new Vec2(next.x - current.x, next.y - current.y);
		let cross = (left.x * right.y) - (left.y * right.x);
		return cross > 0;
    }

    public static GetBounds(points: Vec2[]) {
        const bounds = {
			minX: Infinity,
			maxX: -Infinity,
			minY: Infinity,
			maxY: -Infinity
		};

		for (const point of points) {
			bounds.minX = Math.min(bounds.minX, point.x);
			bounds.maxX = Math.max(bounds.maxX, point.x);
			bounds.minY = Math.min(bounds.minY, point.y);
			bounds.maxY = Math.max(bounds.maxY, point.y);
		}
		return bounds;
    }

    public static SegmentsCross(a: Vec2, b: Vec2, c: Vec2, d: Vec2): boolean {
        let denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x));
		if (denominator == 0){
            return false;
		}
	    let numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y));
	    let numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
	    if (numerator1 == 0 || numerator2 == 0){
            return false;
	    }
	    let r = numerator1 / denominator;
	    let s = numerator2 / denominator;
	    return (r > 0 && r < 1) && (s > 0 && s < 1);
    }

    public static SegmentsIntersect(a:Vec2, b:Vec2, c:Vec2, d:Vec2):Vec2
	{
		let x1 = a.x
		let y1 = a.y;
		let x2 = b.x
		let y2 = b.y;
		let x3 = c.x
		let y3 = c.y;
		let x4 = d.x
		let y4 = d.y;
	
		const maxX1X2 = Math.max(x1, x2);
		const minX1X2 = Math.min(x1, x2);
		const maxX3X4 = Math.max(x3, x4);
		const minX3X4 = Math.min(x3, x4);
		const maxY1Y2 = Math.max(y1, y2);
		const minY1Y2 = Math.min(y1, y2);
		const maxY3Y4 = Math.max(y3, y4);
		const minY3Y4 = Math.min(y3, y4);
	
		if (!(maxX1X2 >= minX3X4 && maxX3X4 >= minX1X2 && maxY1Y2 >= minY3Y4 && maxY3Y4 >= minY1Y2)) {
			return null;
		}
	
		const u = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3));
		const v = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3));
		const w = ((x2 - x1) * (y3 - y4) - (y2 - y1) * (x3 - x4));
		const t = ((x4 - x3) * (y2 - y1) - (y4 - y3) * (x2 - x1));
	
		if((u * t < 0) && (v * w < 0))
		{
			let pos = this.SindIntersectionPoint(a, b, c, d);
			if(pos.x >= minX1X2 && pos.x <= maxX1X2 && pos.y >= minY1Y2 && pos.y <= maxY1Y2)
			{
				return pos;
			}
		}
		return null;
	}
	
	public static SindIntersectionPoint(a:Vec2, b:Vec2, c:Vec2, d:Vec2):Vec2
	{
		let denominator = (b.y - a.y)*(d.x - c.x) - (a.x - b.x)*(c.y - d.y);  
	
		if (denominator === 0) {
			return null; 
		}
	
		let x = ( (b.x - a.x) * (d.x - c.x) * (c.y - a.y) + (b.y - a.y) * (d.x - c.x) * a.x - (d.y - c.y) * (b.x - a.x) * c.x ) / denominator ;  
		let y = -( (b.y - a.y) * (d.y - c.y) * (c.x - a.x) + (b.x - a.x) * (d.y - c.y) * a.y - (d.x - c.x) * (b.y - a.y) * c.y ) / denominator; 
		return new Vec2(x, y);
	}

	public static IsClockwise(polygon:Vec2[]) {  
		const numVertices = polygon.length;  
		if (numVertices < 3) {  
			throw new Error("Polygon must have at least 3 vertices.");  
		}  
	  
		let area = 0;  
	  
		for (let i = 0; i < numVertices; i++) {  
			const x1 = polygon[i].x;  
			const y1 = polygon[i].y;  
			const x2 = polygon[(i + 1) % numVertices].x;  
			const y2 = polygon[(i + 1) % numVertices].y;  
	  
			area += (x2 - x1) * (y2 + y1);  
		}  
	  
		return area >= 0;  
	}
}